import 'dart:collection';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

/// 一个通用的InheritedWidget，保存需要跨组件共享的状态
class InheritedProvider<T> extends InheritedWidget {
  InheritedProvider({
    required this.data,
    required Widget child,
  }) : super(child: child);

  final T data;

  @override
  bool updateShouldNotify(InheritedProvider<T> old) {
    //在此简单返回true，则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。
    return true;
  }
}

///数据保存的地方有了，那么接下来我们需要做的就是在数据发生变化的时候来重新构建InheritedProvider，那么现在就面临两个问题：
///
/// 数据发生变化怎么通知？
///谁来重新构建InheritedProvider？
/// 第一个问题其实很好解决，我们当然可以使用之前介绍的eventBus来进行事件通知，
///
/// 但是为了更贴近Flutter开发，我们使用Flutter SDK中提供的ChangeNotifier类 ，它继承自Listenable
/// ，也实现了一个Flutter风格的发布者-订阅者模式，ChangeNotifier定义大致如下：
///

class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
  ChangeNotifierProvider({
    Key? key,
    required this.data,
    required this.child,
  });

  final Widget child;
  final T data;

  //定义一个便捷方法，方便子树中的widget获取共享数据
  // static T of<T>(BuildContext context) {
  //   // final type = _typeOf<InheritedProvider<T>>();
  //   final provider =
  //       context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();
  //   return provider!.data;
  // }

  //添加一个listen参数，表示是否建立依赖关系
  ///dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的区别就是前者会注册依赖关系，而后者不会。
  static T of<T>(BuildContext context, {bool listen = true}) {
    final provider = listen
        ? context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>()
        : context
            .getElementForInheritedWidgetOfExactType<InheritedProvider<T>>()
            ?.widget as InheritedProvider<T>;
    return provider!.data;
  }

  @override
  _ChangeNotifierProviderState<T> createState() =>
      _ChangeNotifierProviderState<T>();
}

class _ChangeNotifierProviderState<T extends ChangeNotifier>
    extends State<ChangeNotifierProvider<T>> {
  void update() {
    print("_ChangeNotifierProviderState update");
    //如果数据发生变化（model类调用了notifyListeners），重新构建InheritedProvider
    setState(() => {});
  }

  @override
  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
    print(
        "_ChangeNotifierProviderState didUpdateWidget 数据 ${widget.data} 老数据${oldWidget.data}");
    //当Provider更新时，如果新旧数据不"=="，则解绑旧数据监听，同时添加新数据监听
    if (widget.data != oldWidget.data) {
      oldWidget.data.removeListener(update);
      widget.data.addListener(update);
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  void initState() {
    // 给model添加监听器
    print("_ChangeNotifierProviderState initState");
    widget.data.addListener(update);
    super.initState();
  }

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print("_ChangeNotifierProviderState didChangeDependencies");
  }

  @override
  void dispose() {
    // 移除model的监听器
    print("_ChangeNotifierProviderState dispose");
    widget.data.removeListener(update);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print("_ChangeNotifierProviderState build");
    return InheritedProvider<T>(
      data: widget.data,
      child: widget.child,
    );
  }
}

///下面实现一个购物车示例，最大的 意义在于跨路由界面实现 数据通信
///
class Item {
  Item(this.price, this.count);

  double price; //商品单价
  int count; // 商品份数
//... 省略其它属性
}

class CartModel extends ChangeNotifier {
  // 用于保存购物车中商品列表
  final List<Item> _items = [];

  // 禁止改变购物车里的商品信息，一种禁止修改的ListView，比如电商app购物车里面的物品是禁止修改的。
  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);

  // 购物车中商品的总价
  double get totalPrice =>
      _items.fold(0, (value, item) => value + item.count * item.price);

  // 将 [item] 添加到购物车。这是唯一一种能从外部改变购物车的方法。
  void add(Item item) {
    _items.add(item);
    // 通知监听器（订阅者），重新构建InheritedProvider， 更新状态。
    notifyListeners();
  }
}

class ProviderRoute extends StatefulWidget {
  @override
  _ProviderRouteState createState() => _ProviderRouteState();
}

class _ProviderRouteState extends State<ProviderRoute> {
  GlobalKey<_ChangeNotifierProviderState> providerGlobalKey = GlobalKey();
  @override
  void initState() {
    // TODO: implement initState
    super.initState();

  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: ChangeNotifierProvider<CartModel>(
          key:providerGlobalKey,
      data: CartModel(),
      child: Column(
        children: <Widget>[
          ///Builder(builder: (context){
          ///   var cart=ChangeNotifierProvider.of<CartModel>(context);
          ///   return Text("总价: ${cart.totalPrice}");
          /// })
          /// Consumer 相当于上面这一块
          Consumer<CartModel>(builder: (context, cart) {
            return Text("总价: ${cart!.totalPrice}");
          }),
          Builder(builder: (context) {
            print("ElevatedButton build"); //在后面优化部分会用到
            return ElevatedButton(
              child: Text("添加商品"),
              onPressed: () {
                //给购物车中添加商品，添加后总价会更新
                ChangeNotifierProvider.of<CartModel>(context, listen: false)
                    .add(Item(20.0, 1));
              },
            );
          }),
          TextButton(
            child: Text("去子控件更改全局共享数据"),
            onPressed: () {
              var cart = CartModel();
              cart.add(Item(20.0, 1));
              var result =
                  Navigator.push(context, MaterialPageRoute(builder: (context) {
                return _ChildChangeDataRout(
                  card: cart,
                  providerGlobalKey: providerGlobalKey,
                );
              }));
            },
          ),
        ],
      ),
    ));
  }
}

class Consumer<T> extends StatelessWidget {
  Consumer({
    Key? key,
    required this.builder,
  }) : super(key: key);

  final Widget Function(BuildContext context, T? value) builder;

  @override
  Widget build(BuildContext context) {
    return builder(
      context,
      ChangeNotifierProvider.of<T>(context),
    );
  }
}

// 这是一个便捷类，会获得当前context和指定数据类型的Provider
class _ChildChangeDataRout extends StatefulWidget {
  _ChildChangeDataRout({Key? key, required this.card,required this.providerGlobalKey}) : super(key: key);

  CartModel card;
  GlobalKey<_ChangeNotifierProviderState> providerGlobalKey;

  @override
  _ChildChangeDataState createState() {
    return _ChildChangeDataState();
  }
}

class _ChildChangeDataState extends State<_ChildChangeDataRout> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<CartModel>(
        // data: widget.providerGlobalKey.currentState!.widget.data as CartModel,
        data: widget.card,
        child: Builder(builder: (context) {
          print("ElevatedButton build"); //在后面优化部分会用到
          return TextButton(
            // child: Text("子控件更改试试看${(widget.providerGlobalKey.currentState!.widget.data as CartModel).totalPrice}"),
            child: Text("子控件更改试试看${widget.card.totalPrice}"),
            onPressed: () {
              ChangeNotifierProvider.of<CartModel>(context).add(Item(20.0, 1));
            },
          );
        }));
  }
}
